local blaze = require "blaze"
local gamelib = require "club.threecard.gamelib"
local slotlib = require "club.threecard.slotlib"

local column = slotlib.require "column"
local line = slotlib.require "line"
local default_config = slotlib.require "machine_config_default"
local default_sfx = slotlib.require "sound_effect"

local Vector2 = CS.UnityEngine.Vector2;
local Vector3 = CS.UnityEngine.Vector3;

local Ease = CS.DG.Tweening.Ease

local log = gamelib.logging.get("slotlib")

local machine = {}

local machines = {}

function machine.init()

end

function machine.release()
    line.release();

    for _, slotMachine in pairs(machines) do
        slotMachine:release_self();
    end
end

local function merge_sfx(sfx)
    local ret = {};
    if sfx then
        for k, v in pairs(sfx) do
            ret[k] = v;
        end
    end

    for k, v in pairs(default_sfx) do
        if not ret[k] then
            ret[k] = v;
        end
    end

    return ret;
end

local function merge_config(client_config, server_config)
    local paylines = {};
    for _, payline in ipairs(server_config.machine.pay_lines) do
        table.insert(paylines, payline.indexes);
    end
    client_config.paylines = paylines;
    
    client_config.pay_rules = server_config.machine.pay_rules

    for index, reel in ipairs(server_config.machine.reels) do
        local symbol_indexs = {};
        for _, stop in ipairs(reel.stops) do
            table.insert(symbol_indexs, stop.symbol_id);
        end
        client_config.columns[index].symbol_indexs = symbol_indexs;
    end
end

function machine.create(config, sfx)
    config = config or default_config;

    local _, server_config = gamelib.roomcache.get(gamelib.game_scene.room_config_id);
    merge_config(config, server_config);

    if machines[config] then
        return machines[config]
    end

    local ret = 
    {
        columns = {};
        loaded = false;
        config = config;
        sfx = merge_sfx(sfx);
    };

    for index, column_config in ipairs(config.columns) do
        local column = column.new(ret, index, column_config, config.symbols);
        ret.columns[index] = column;
    end

    setmetatable(ret, {__index = machine});
    
    machines[config] = ret;
    return ret;
end

function machine:draw(indexes)
    local elements = {};
    for num, column in ipairs(self.columns) do
        local tar_index = indexes[num];
        if tar_index > #column.elements then
            tar_index = tar_index % #column.elements;
        end
        for _, element in ipairs(column.elements) do
            if element.index == tar_index then
                table.insert(elements, element);
                break;
            end
        end
    end

    return line.draw(elements);
end

function machine:active(indexes)
    local elements = {};
    for num, column in ipairs(self.columns) do
        local tar_index = indexes[num];
        if tar_index > #column.elements then
            tar_index = tar_index % #column.elements;
        end

        for _, element in ipairs(column.elements) do
            if element.index == tar_index then
                table.insert(elements, element);
            end
            element:active(false);
        end
    end

    for _, element in ipairs(elements) do
        element:active(true);
    end
end

function machine:get_elements(indexes)
    local elements = {};
    for num, column in ipairs(self.columns) do
        local tar_index = indexes[num];
        if tar_index > #column.elements then
            tar_index = tar_index % #column.elements;
        end

        for _, element in ipairs(column.elements) do
            if element.index == tar_index then
                table.insert(elements, element);
            end
        end
    end

    return elements;
end

function machine:get_elements_xy(indexes)
    local elements = {};

    for _, xy in ipairs(indexes) do
        local b = false;
        for x, column in ipairs(self.columns) do
            if b then
                break;
            end

            local tar_y = xy.y;
            if tar_y > #column.elements then
                tar_y = tar_y % #column.elements;
            end

            for _, element in ipairs(column.elements) do
                if xy.x == x and tar_y == element.index then
                    table.insert(elements, element);
                    b = true;
                    break;
                end
            end
        end
    end

    return elements
end

function machine:deactive_all()
    for num, column in ipairs(self.columns) do
        for _, element in ipairs(column.elements) do
            element:active(false);
        end
    end
end

function machine:start()
    line.clear();

    for num, column in ipairs(self.columns) do
        for _, element in ipairs(column.elements) do
            element:active(false);
        end
    end

    self.running = true;
    for _, column in ipairs(self.columns) do
        column:start();
    end

    self.sfx.spin_start();
    self.sfx.reels_spinning();
end

function machine:stop(indexes, overHandler)
    assert(#self.columns == #indexes);

    if self.stop_co then
        log.warn("正在停止...");
        return;
    end

    self.stop_co = gamelib.coroutine.start(function()
        local leftCount = #self.columns;
        for index, column in ipairs(self.columns) do
            column:stop(indexes[index], function()
                leftCount = leftCount - 1;
            end);
        end

        while leftCount > 0 do
            gamelib.coroutine.step();    
        end

        self.running = false;
        self.stop_co = nil;
        self.sfx.reels_stop();

        if overHandler then
            overHandler();
        end
    end)
end

function machine:stop_immediate()
    for index, column in ipairs(self.columns) do
        column:stop_immediate();
    end
end

-- 马上停止
function machine:shutdown()
    line.clear();
    if not self.running then
        return;
    end

    self.sfx.reels_stop();
    if self.stop_co then
        gamelib.coroutine.stop(self.stop_co);
        self.stop_co = nil;
    end

    for index, column in ipairs(self.columns) do
        column:shutdown();
    end
    self.running = false;
end

function machine:load(root)
    if self.loaded then
        return;
    end

    root.sizeDelta = Vector2(self.config.window_width, self.config.window_height);
    local start_x = -self.config.window_width/2;
    local start_y = self.config.window_height/2;
    start_x = start_x + self.config.start_padding_x;
    start_y = start_y + self.config.start_padding_y;
    start_x = start_x + self.config.symbol_width/2;
    start_y = start_y - self.config.symbol_height/2;

    for index, column in ipairs(self.columns) do
        local x = start_x + self.config.symbol_spacing_x * (index-1)
        for index2, element in ipairs(column.elements) do
            local y = start_y - self.config.symbol_spacing_y * (index2 - column.config.first_on_port);
            element:set_parent(root);
            if column.config.offset then
                y = y + column.config.offset;
            end
            element:set_position(Vector3(x, y, 0));
        end
    end

    for index, column in ipairs(self.columns) do
        column:load();
    end

    self.loaded = true;
end

function machine:release_self()
    if not self.loaded then
        return;
    end

    self.sfx.reels_stop();
    for index, column in ipairs(self.columns) do
        column:release();
    end
    
    self.loaded = false;
end

return machine